#pragma once  

#include <list>
#include <thread>
#include <functional>
#include <atomic>
#include <memory>
#include <chrono>
#include "SyncQueue.h"

//#include "../../Log/TraceLog.h"

const int MaxTaskCount = 100;
class QueueThreadPool
{
public:
	using Task = std::function<void()>;

	QueueThreadPool(int numThreads = std::thread::hardware_concurrency()) : m_taskQueue(MaxTaskCount)
	{
		StartThreadGroup(numThreads);
	}

	~QueueThreadPool()
	{
		Stop();
	}

	void Stop()
	{
		std::call_once(m_flag, [this] {StopThreadGroup(); });
	}

	void AddTask(Task&& task)
	{
		//TRACE_LOG_A(nullptr, TRACE_LOG_LEVEL_DEBUG, "void AddTask(Task&& task)");
		m_taskQueue.Put(std::forward<Task>(task));
	}

	void AddTask(const Task& task)
	{
		//TRACE_LOG_A(nullptr, TRACE_LOG_LEVEL_DEBUG, "void AddTask(const Task& task)");
		m_taskQueue.Put(task);
	}
private:
	void StartThreadGroup(int numThreads)
	{
		m_running = true;
		for (int i = 0; i < numThreads; i++)
		{
			m_threadGroup.push_back(std::make_shared<std::thread>(&QueueThreadPool::RunInThread, this));
		}
	}

	void RunInThread()
	{
		while (m_running)
		{
			std::list<Task> list;
			m_taskQueue.Take(list);

			for (auto& task : list)
			{
				if (!m_running)
					return;

				//std::this_thread::sleep_for(std::chrono::milliseconds(3000));
				task();
			}
		}
	}

	void StopThreadGroup()
	{
		m_taskQueue.Stop();
		m_running = false;

		for (auto thread : m_threadGroup)
		{
			if (thread)
				thread->join();
		}
		m_threadGroup.clear();
	}
private:
	std::list<std::shared_ptr<std::thread>> m_threadGroup;
	SyncQueue<Task> m_taskQueue;
	std::atomic_bool m_running;
	std::once_flag m_flag;

};